/*************************************************************************
	> File Name: ProcessPool.cc
	> Author: H.M. Lau
	> Mail: hm.lau@outlook.com
	> Created Time: 2023/09/04 21:03
 ************************************************************************/

#include "Task.hpp"

#define PROCESS_NUM 5

enum option { EXIT, SHOW, EXEC };

int waitCommand(int waitFd, bool &quit)
{
    uint32_t command = 0;
    ssize_t s = read(waitFd, &command, sizeof command);
    if (s == 0) {
        quit = true;
        return -1;
    }
    assert(s == sizeof(uint32_t));
    return command;
}

void sendAndWakeup(pid_t who, int fd, uint32_t command)
{
    write(fd, &command, sizeof command);
    std::cout << "Main process:call process[" << who << "] execute " << desc[command] << " through " << fd << std::endl;
}

void menu()
{
    std::cout << std::endl;
    std::cout << "###################################################" << std::endl;
    std::cout << "#  1. Show Function   2. Send Command    0. Exit  #" << std::endl;
    std::cout << "###################################################" << std::endl;
    std::cout << std::endl;
}

int main(int argc, char *argv[])
{
    load();
    std::vector<std::pair<pid_t, int>> slots;
    for (int i = 0; i < PROCESS_NUM; ++i) {
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        assert(n == 0);
        (void)n;
        pid_t id = fork();
        assert(id != -1);
        if (id == 0) {
            close(pipefd[1]);
            while (1) {
                bool quit = false;
                int command = waitCommand(pipefd[0], quit);
                if (quit) break; 
                if (command >= 0 && command < handlerSize()) {
                    callbacks[command]();
                } else {
                    std::cout << "Illegal command." << std::endl;
                } 
            }
            exit(1);
        }
        close(pipefd[0]);
        slots.push_back(std::pair<pid_t, int>(id, pipefd[1]));
    } 
    srand((unsigned long)time(nullptr) ^ getpid());
#ifdef RAND
    while (1) {
        int command = rand() % handlerSize();
        int choice = rand() % slots.size();
        sendAndWakeup(slots[choice].first, slots[choice].second, command);
        sleep(1);
    }
#endif
#ifdef SELECT
    int select = 0;
    int command;
    do {
        menu();
        std::cout << "Please Select> ";
        std::cin >> select;
        switch (select) {
            case SHOW:
                showHandler();
                break;
            case EXEC: {
                std::cout << "Input Your Command> ";
                std::cin >> command;
                int choice = rand() % slots.size();
                sendAndWakeup(slots[choice].first, slots[choice].second, command);
            }   break;
            case EXIT:
                break;
            default:
                std::cout << "Input illegal, please input again." << std::endl;
                break; 
        }
        usleep(100);
    } while (select);
#endif
    for (const auto &slot : slots) {
        close(slot.second);
    }
    for (const auto &slot : slots) {
        waitpid(slot.first, nullptr, 0);
    }
    return 0;
}
